package com.ffox.filter;

import com.ffox.jjwt.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * JWT认证过滤器，用于拦截请求并验证JWT Token，设置Spring Security的认证信息。
 * 该过滤器继承自OncePerRequestFilter，确保每个请求只被处理一次。
 */
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtils jwtUtils;

    private static final String TOKEN_COOKIE_NAME = "access_token";

    /**
     * 判断是否应该跳过JWT认证过滤。
     * 排除登录相关路径和WebSocket相关路径。
     *
     * @param request 当前HTTP请求对象
     * @return true表示跳过过滤，false表示需要进行JWT认证
     */
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        System.out.println("进入jwt shouldNotFilter");
        String path = request.getRequestURI();
        // 排除：登录路径 + WebSocket 相关路径（预检 + 握手）
        return path.startsWith("/login/platformLogin")
                || path.startsWith("/login/schoolLogin")
                || path.startsWith("/login/userLogin")
                || path.startsWith("/login/studentLogin")
                || path.startsWith("/login/userInsert")
                || path.startsWith("/login/schoolInsert")
                || path.startsWith("/login/studentInsert")
                || path.startsWith("/ws");
    }

    /**
     * 执行JWT认证的核心逻辑。
     * 从请求头或Cookie中提取Token，验证Token有效性，并设置Spring Security上下文中的认证信息。
     *
     * @param request  当前HTTP请求对象
     * @param response 当前HTTP响应对象
     * @param filterChain 过滤器链，用于继续执行后续过滤器
     * @throws ServletException Servlet异常
     * @throws IOException IO异常
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        System.out.println("JwtAuthenticationFilter===>拦截了请求: " + request.getRequestURI());

        try {
            // 检查是否已有认证信息
            Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
            if (existingAuth != null && existingAuth.isAuthenticated()) {
                System.out.println("已有认证信息，用户: " + existingAuth.getName());
                filterChain.doFilter(request, response);
                return;
            }

            // 1. 从 Authorization 头获取 Token
            String token = getTokenFromAuthorizationHeader(request);
            // 2. 从 Cookie 获取 Token
            if (token == null) {
                token = getTokenFromCookie(request);
            }
            System.out.println("验证前的Token: " + (token == null ? "null" : "******"));

            // 3. 验证 Token 并设置认证
            if (token != null && jwtUtils.validateToken(token)) {
                Claims claims = jwtUtils.parseToken(token);
                String username = claims.getSubject();
                List<String> roles = claims.get("roles", List.class);
                System.out.println("解析的用户名: " + username + ", 角色: " + roles);

                // 将角色转换为SimpleGrantedAuthority列表
                List<SimpleGrantedAuthority> authorities = roles.stream()
                        .map(role -> role.startsWith("ROLE_") ?
                                new SimpleGrantedAuthority(role) :
                                new SimpleGrantedAuthority("ROLE_" + role))
                        .collect(Collectors.toList());

                // 构建认证对象
                UsernamePasswordAuthenticationToken authentication =
                        new UsernamePasswordAuthenticationToken(username, null, authorities);
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                // 设置安全上下文
                SecurityContext context = SecurityContextHolder.createEmptyContext();
                context.setAuthentication(authentication);
                SecurityContextHolder.setContext(context);
                System.out.println("认证信息已设置: " + username);
            } else {
                System.out.println("Token不存在或验证失败");
                SecurityContextHolder.clearContext();
            }
        } catch (Exception e) {
            logger.error("JWT认证处理失败", e);
            SecurityContextHolder.clearContext();
            if (!shouldNotFilter(request)) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.setHeader("WWW-Authenticate", null);
                response.getWriter().write("认证处理失败");
                return;
            }

        }

        filterChain.doFilter(request, response);
    }

    /**
     * 从Authorization请求头中提取JWT Token。
     *
     * @param request 当前HTTP请求对象
     * @return 提取到的Token字符串，若未找到则返回null
     */
    private String getTokenFromAuthorizationHeader(HttpServletRequest request) {
        String authHeader = request.getHeader("Authorization");
        System.out.println("Authorization头: " + authHeader);
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            return authHeader.substring(7);
        }
        return null;
    }

    /**
     * 从Cookie中提取JWT Token。
     *
     * @param request 当前HTTP请求对象
     * @return 提取到的Token字符串，若未找到则返回null
     */
    private String getTokenFromCookie(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (TOKEN_COOKIE_NAME.equals(cookie.getName())) {
                    String token = cookie.getValue();
                    System.out.println("从Cookie提取的Token: " + token);
                    return token;
                }
            }
        }
        System.out.println("Cookie中未找到Token（名称: " + TOKEN_COOKIE_NAME + "）");
        return null;
    }
}
